<template>
  <div id="main">
    <video id="remoteVideo" playsinline autoplay></video>
    <video id="localVideo" playsinline autoplay muted></video>
    <div id="buttons">
      <input id="myid" v-model="username" />
      <input id="toUser" placeholder="输入在线好友账号" />
      <button id="call" @click="call(true)">视频通话</button>
      <button id="deskcall" @click="call(false)">分享桌面</button>
      <button id="hangup" @click="hangup">挂断</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'VideoCall',
  data() {
    return {
      username: '',
      localVideo: null,
      remoteVideo: null,
      websocket: null,
      peer: {},
      stream: null,
      callType: true,
      mediaRecorder: null,
      recordedBlobs: [],
      startTime: 0
    };
  },
  methods: {
    generateRandomLetters(length) {
      let result = '';
      const characters = 'abcdefghijklmnopqrstuvwxyz';
      for (let i = 0; i < length; i++) {
        const randomIndex = Math.floor(Math.random() * characters.length);
        const randomLetter = characters[randomIndex];
        result += randomLetter;
      }
      return result;
    },
    WebSocketInit() {
      if ('WebSocket' in window) {
        this.username = this.generateRandomLetters(2);
        this.$data.localVideo = document.getElementById('localVideo');
        this.$data.remoteVideo = document.getElementById('remoteVideo');
        this.websocket = new WebSocket("ws://localhost:8080/video/webrtc/" + this.username);
        this.websocket.onerror = (e) => {
          alert("WebSocket连接发生错误！");
        };
        this.websocket.onclose = () => {
          console.error("WebSocket连接关闭");
        };
        this.websocket.onopen = () => {
          console.log("WebSocket连接成功");
        };
        this.websocket.onmessage = async (event) => {
          let { type, fromUser, toUser, msg, sdp, iceCandidate } = JSON.parse(event.data.replace(/\n/g, "\\n").replace(/\r/g, "\\r"));
          console.log(type, fromUser, toUser);
          if (type === 'hangup') {
            console.log(msg);
            this.hangup();
            return;
          }
          if (type === 'call_start') {
            this.hangup();
            let msg = "0";
            if (confirm(fromUser + "发起视频通话，确定接听吗") == true) {
              document.getElementById('toUser').value = fromUser;
              this.WebRTCInit(fromUser);
              msg = "1";
              document.getElementById('toUser').style.visibility = 'hidden';
              document.getElementById('myid').style.visibility = 'hidden';
            }
            this.websocket.send(JSON.stringify({
              type: "call_back",
              toUser: fromUser,
              fromUser: this.username,
              msg: msg
            }));
            return;
          }
          if (type === 'call_back') {
            if (msg === "1") {
              console.log(document.getElementById('toUser').value + "同意视频通话");
              await this.getVideo(this.callType);
              this.stream.getTracks().forEach(track => {
                this.peer[fromUser].addTrack(track, this.stream);
              });
              let offer = await this.peer[fromUser].createOffer();
              await this.peer[fromUser].setLocalDescription(offer);
              let newOffer = {...offer};
              newOffer["fromUser"] = this.username;
              newOffer["toUser"] = document.getElementById('toUser').value;
              this.websocket.send(JSON.stringify(newOffer));
            } else if (msg === "0") {
              alert(document.getElementById('toUser').value + "拒绝视频通话");
              this.hangup();
            } else {
              alert(msg);
              this.hangup();
            }
            return;
          }
          if (type === 'offer') {
            await this.getVideo(this.callType);
            this.stream.getTracks().forEach(track => {
              this.peer[fromUser].addTrack(track, this.stream);
            });
            await this.peer[fromUser].setRemoteDescription(new RTCSessionDescription({ type, sdp }));
            let answer = await this.peer[fromUser].createAnswer();
            let newAnswer = {...answer};
            newAnswer["fromUser"] = this.username;
            newAnswer["toUser"] = fromUser;
            this.websocket.send(JSON.stringify(newAnswer));
            await this.peer[fromUser].setLocalDescription(newAnswer);
            return;
          }
          if (type === 'answer') {
            this.peer[fromUser].setRemoteDescription(new RTCSessionDescription({ type, sdp }));
            return;
          }
          if (type === '_ice') {
            this.peer[fromUser].addIceCandidate(iceCandidate);
            return;
          }
          if (type === 'getStream') {
            this.WebRTCInit(fromUser);
            this.stream.getTracks().forEach(track => {
              this.peer[fromUser].addTrack(track, this.stream);
            });
            let offer = await this.peer[fromUser].createOffer();
            await this.peer[fromUser].setLocalDescription(offer);
            let newOffer = {...offer};
            newOffer["fromUser"] = this.username;
            newOffer["toUser"] = fromUser;
            this.websocket.send(JSON.stringify(newOffer));
          }
        };
      } else {
        alert("当前浏览器不支持WebSocket！");
      }
    },
    call(ct) {
      let toUser = document.getElementById('toUser').value;
      if (!toUser) {
        alert("请先指定好友账号，再发起视频通话！");
        return;
      }
      this.callType = ct;
      document.getElementById('toUser').style.visibility = 'hidden';
      document.getElementById('myid').style.visibility = 'hidden';
      if (!this.peer[toUser]) {
        this.WebRTCInit(toUser);
      }
      this.websocket.send(JSON.stringify({
        type: "call_start",
        fromUser: this.username,
        toUser: toUser,
      }));
    },
    hangup() {
      document.getElementById('toUser').style.visibility = 'unset';
      document.getElementById('myid').style.visibility = 'unset';
      if (this.stream) {
        this.stream.getTracks().forEach(track => {
          track.stop();
        });
        this.stream = null;
      }
      if (this.remoteVideo.srcObject) {
        this.remoteVideo.srcObject.getTracks().forEach(track => track.stop());
        this.websocket.send(JSON.stringify({
          type: "hangup",
          fromUser: this.username,
          toUser: document.getElementById('toUser').value,
        }));
      }
      Object.values(this.peer).forEach(peer => peer.close());
      this.peer = {};
      this.localVideo.srcObject = null;
      this.remoteVideo.srcObject = null;
    },
    async getVideo(callType) {
      if (this.stream == null) {
        if (callType) {
          this.stream = await navigator.mediaDevices.getUserMedia({
            video: {
              width: 2560, height: 1440,
              facingMode: "user",
              frameRate: { ideal: 25, max: 30 }
            },
            audio: {
              autoGainControl: true,
              echoCancellation: true,
              noiseSuppression: true
            }
          });
        } else {
          this.stream = await navigator.mediaDevices.getDisplayMedia({ video: true });
        }
        this.startRecorder(this.stream);
        this.localVideo.srcObject = this.stream;
      }
    },
    WebRTCInit(userId) {
      if (this.peer[userId]) {
        this.peer[userId].close();
      }
      const p = new RTCPeerConnection();
      p.onicecandidate = (e) => {
        if (e.candidate) {
          this.websocket.send(JSON.stringify({
            type: '_ice',
            toUser: userId,
            fromUser: this.username,
            iceCandidate: e.candidate
          }));
        }
      };
      p.ontrack = (e) => {
        if (e && e.streams) {
          console.log('Received remote stream',e);
          this.remoteVideo.srcObject = e.streams[0];
        }
      };
      this.peer[userId] = p;
    },
    startRecorder(stream) {
      this.mediaRecorder = new MediaRecorder(stream);
      this.mediaRecorder.onstart = () => {
        this.recordedBlobs = [];
      };
      this.mediaRecorder.ondataavailable = (event) => {
        if (event.data && event.data.size > 0) {
          this.recordedBlobs.push(event.data);
        }
      };
      this.mediaRecorder.onstop = () => {
        console.log(this.recordedBlobs[0].type);
        const blob = new Blob(this.recordedBlobs, { type: 'video/mp4;codecs=vp8,opus' });
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        const filename = 'recorded-video.mp4';
        a.style.display = 'none';
        a.href
          = url;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        window.URL.revokeObjectURL(url);
      };
      this.mediaRecorder.start();
      this.startTime = Date.now();
    }
  },
  mounted() {
    this.username = this.generateRandomLetters(2);
    this.localVideo = document.getElementById('localVideo');
    this.remoteVideo = document.getElementById('remoteVideo');
    this.WebSocketInit();
  }
};
</script>
<style scoped>
/* Reset margin and padding for the entire document */
html, body {
  margin: 0;
  padding: 0;
  font-family: Arial, sans-serif;
  background-color: #f4f4f4;
  height: 100%;
}

/* Main container, make sure everything is centered and full-screen */
#main {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

/* Video wrapper to maintain aspect ratio */
#videoWrapper {
  position: relative;
  width: 100%;
  height: 100%;
  max-width: 1100px;
  max-height: 700px;
  display: flex;
  justify-content: center;
  align-items: center;
}

/* Local video element (small window in the corner) */
#localVideo {
  position: absolute;
  background: #757474;
  top: 10px;
  right: 10px;
  width: 180px;
  height: 135px;
  border-radius: 8px;
  border: 2px solid #fff;
  z-index: 2;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
}

/* Remote video element (background video) */
#remoteVideo {
  width: 100%;
  height: 100%;
  background: #222;
  border-radius: 8px;
}

/* Buttons container */
#buttons {
  position: absolute;
  bottom: 20px; /* 距离底部20px */
  right: 20px; /* 距离右侧20px */
  z-index: 10;
  display: flex;
  flex-direction: row; /* 改为水平排列 */
  align-items: center; /* 垂直居中对齐 */
  gap: 10px; /* 按钮之间的间隔 */
}

/* Input field for user search */
input {
  border: 1px solid #ccc;
  padding: 10px;
  border-radius: 5px;
  width: 200px;
  font-size: 14px;
  background-color: #fff;
  transition: border-color 0.3s ease;
}

/* Focus style for input elements */
input:focus {
  border-color: #66afe9;
  outline: none;
  box-shadow: 0 0 8px rgba(102, 175, 233, 0.6);
}

/* Button styles */
button {
  width: 140px;
  height: 45px;
  background-color: #6de73d;
  color: white;
  border: none;
  border-radius: 8px;
  font-size: 16px;
  cursor: pointer;
  transition: background-color 0.3s ease, transform 0.1s ease;
  display: flex;
  justify-content: center;
  align-items: center;
}

/* Hover effect for buttons */
button:hover {
  background-color: #5ac735;
  transform: scale(1.05);
}

/* Call button style */
#call {
  background-color: #00BB00;
}

#call:hover {
  background-color: #00a500;
}

/* Hangup button style */
#hangup {
  background-color: #FF5151;
}

#hangup:hover {
  background-color: #ff2a2a;
}

/* Desktop share button style */
#deskcall {
  background-color: #f4a261;
}

#deskcall:hover {
  background-color: #e07b4e;
}


</style>
